#include <algorithm>
#include "trm/segment.h"
#include "trm/twomasks.h"

namespace trm {

segment::segment(int org, icache::footage_sptr const & f)
{
	reset(org, f);
}

segment::~segment()
{
}

void segment::reset(int org, icache::footage_sptr const & f)
{
	_origin = org;
	_accessible_span = span(org, org);
	clear();
	_footage = f;
	_param.set_default_values();
}

void segment::clear()
{
	_masks.clear();
	_masks.resize(_accessible_span.size());
	_bit_masks_span = _grey_masks_span = span(_origin, _origin);
	_timing.clear();
}

void segment::trim(span const & t)
{
	for (int i = _accessible_span.first; i < _accessible_span.last; ++i) {
		if (!t.inside(i))
			reset_frame_masks(i);
	}
	_bit_masks_span.intersect(t);
	_grey_masks_span.intersect(t);
}

void segment::accessible_span(span const & aspan)
{
	span intsec(aspan.intersection(_accessible_span));
	masks_vector vec(aspan.size());
	std::copy(
		_masks.begin() + intsec.first - _accessible_span.first,
		_masks.begin() + intsec.last - _accessible_span.first,
		vec.begin() + intsec.first - aspan.first);
	_masks.swap(vec);

	_accessible_span = aspan;
}

bool segment::param(trm::param p)
{
	p.put_within_the_limits();
	if (_param == p)
		return false;

	bool erase_all = 
		_param.smooth_mask && !_param.smooth_decorative ||
		p.smooth_mask && !p.smooth_decorative;
	if (!erase_all) {
		_param.smooth_mask = p.smooth_mask;
		_param.smooth_window_half_size = p.smooth_window_half_size;
		_param.smooth_passes_num = p.smooth_passes_num;
		_param.smooth_max_condition_number = p.smooth_max_condition_number;
		//_param.smooth_fore_depth = p.smooth_fore_depth;
		//_param.smooth_back_depth = p.smooth_back_depth;
		//_param.smooth_sharpness = p.smooth_sharpness;
		//_param.smooth_noisiness = p.smooth_noisiness;
		erase_all = _param != p;
	}
	if (erase_all) {
		_param = p;
		clear();
		return true;
	}

	for (int i = 0; i != _masks.size(); ++i)
		if (_masks[i])
			_masks[i] = _masks[i]->copy_except_grey();

	_grey_masks_span = span(_origin, _origin);
	return true;
}

void segment::key_mask(const trm::bit_mask & mask)
{
	_key_mask = mask;
	clear();
}

void segment::find_bit_masks_span()
{
	span & s = _bit_masks_span;
	s.first = _origin;
	for (;;) {
		int f = s.first-1;
		if (f < _accessible_span.first)
			break;
		twomasks_sptr const & p = _masks[f - _accessible_span.first];
		if (!p || p->b.empty())
			break;
		s.first = f;
	}
	s.last = _origin;
	for (;;) {
		if (s.last >= _accessible_span.last)
			break;
		twomasks_sptr const & p = _masks[s.last - _accessible_span.first];
		if (!p || p->b.empty())
			break;
		++s.last;
	}
}

void segment::find_grey_masks_span()
{
	span & s = _grey_masks_span;
	s.first = _origin;
	for (;;) {
		int f = s.first-1;
		if (f < _accessible_span.first)
			break;
		twomasks_sptr const & p = _masks[f - _accessible_span.first];
		if (!p || p->g.empty())
			break;
		assert(!p->b.empty());
		s.first = f;
	}
	s.last = _origin;
	for (;;) {
		if (s.last >= _accessible_span.last)
			break;
		twomasks_sptr const & p = _masks[s.last - _accessible_span.first];
		if (!p || p->g.empty())
			break;
		assert(!p->b.empty());
		++s.last;
	}
}

icache::cached_image_sptr segment::load_origin_frame(span request) {
	mswin::timing_mark mark(_timing, "loading origin frame");
	int flags = icache::no_nearby;
	request.intersect(_accessible_span);
	if (request.first < _origin)
		flags |= icache::backward;
	if (request.last > _origin+1)
		flags |= icache::forward;
	return _footage->get(_origin, icache::nearby(flags)); 
}

icache::cached_image_sptr segment::load_frame(int frame, direction following) {
	mswin::timing_mark mark(_timing, "loading frame");
	return _footage->get(frame, (following == backward) ? icache::backward : icache::forward); 
}

} //namespace trm
